home *** CD-ROM | disk | FTP | other *** search
/ PC Open 107 / PC Open 107 CD 1.bin / CD1 / INTERNET / EMAIL / pop file / setup.exe / Proxy / NNTP.pm next >
Encoding:
Perl POD Document  |  2004-12-13  |  16.1 KB  |  506 lines

  1. # POPFILE LOADABLE MODULE
  2. package Proxy::NNTP;
  3.  
  4. use Proxy::Proxy;
  5. @ISA = ("Proxy::Proxy");
  6.  
  7. # ----------------------------------------------------------------------------
  8. #
  9. # This module handles proxying the NNTP protocol for POPFile.
  10. #
  11. # Copyright (c) 2001-2004 John Graham-Cumming
  12. #
  13. #   This file is part of POPFile
  14. #
  15. #   POPFile is free software; you can redistribute it and/or modify
  16. #   it under the terms of the GNU General Public License as published by
  17. #   the Free Software Foundation; either version 2 of the License, or
  18. #   (at your option) any later version.
  19. #
  20. #   POPFile is distributed in the hope that it will be useful,
  21. #   but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. #   GNU General Public License for more details.
  24. #
  25. #   You should have received a copy of the GNU General Public License
  26. #   along with POPFile; if not, write to the Free Software
  27. #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  28. #
  29. #   Modified by     Sam Schinke (sschinke@users.sourceforge.net)
  30. #
  31. # ----------------------------------------------------------------------------
  32.  
  33. use strict;
  34. use warnings;
  35. use locale;
  36.  
  37. # A handy variable containing the value of an EOL for networks
  38.  
  39. my $eol = "\015\012";
  40.  
  41. #----------------------------------------------------------------------------
  42. # new
  43. #
  44. #   Class new() function
  45. #----------------------------------------------------------------------------
  46. sub new
  47. {
  48.     my $type = shift;
  49.     my $self = Proxy::Proxy->new();
  50.  
  51.     # Must call bless before attempting to call any methods
  52.  
  53.     bless $self, $type;
  54.  
  55.     $self->name( 'nntp' );
  56.  
  57.     $self->{child_} = \&child__;
  58.     $self->{connection_timeout_error_} = '500 no response from mail server';
  59.     $self->{connection_failed_error_}  = '500 can\'t connect to';
  60.     $self->{good_response_}            = '^(1|2|3)\d\d';
  61.  
  62.     return $self;
  63. }
  64.  
  65. # ----------------------------------------------------------------------------
  66. #
  67. # initialize
  68. #
  69. # Called to initialize the NNTP proxy module
  70. #
  71. # ----------------------------------------------------------------------------
  72. sub initialize
  73. {
  74.     my ( $self ) = @_;
  75.  
  76.     # Disabled by default
  77.  
  78.     $self->config_( 'enabled', 0);
  79.  
  80.     # By default we don't fork on Windows
  81.  
  82.     $self->config_( 'force_fork', ($^O eq 'MSWin32')?0:1 );
  83.  
  84.     # Default ports for NNTP service and the user interface
  85.  
  86.     $self->config_( 'port', 119 );
  87.  
  88.     # Only accept connections from the local machine for NNTP
  89.  
  90.     $self->config_( 'local', 1 );
  91.  
  92.     # The separator within the NNTP user name is :
  93.  
  94.     $self->config_( 'separator', ':');
  95.  
  96.     # The welcome string from the proxy is configurable
  97.  
  98.     $self->config_( 'welcome_string',
  99.         "NNTP POPFile ($self->{version_}) server ready" );
  100.  
  101.     if ( !$self->SUPER::initialize() ) {
  102.         return 0;
  103.     }
  104.  
  105.     $self->config_( 'enabled', 0 );
  106.  
  107.     return 1;
  108. }
  109.  
  110. # ----------------------------------------------------------------------------
  111. #
  112. # start
  113. #
  114. # Called to start the NNTP proxy module
  115. #
  116. # ----------------------------------------------------------------------------
  117. sub start
  118. {
  119.     my ( $self ) = @_;
  120.  
  121.     # If we are not enabled then no further work happens in this module
  122.  
  123.     if ( $self->config_( 'enabled' ) == 0 ) {
  124.         return 2;
  125.     }
  126.  
  127.     # Tell the user interface module that we having a configuration
  128.     # item that needs a UI component
  129.  
  130.     $self->register_configuration_item_( 'configuration',
  131.                                          'nntp_port',
  132.                                          'nntp-port.thtml',
  133.                                          $self );
  134.  
  135.     $self->register_configuration_item_( 'configuration',
  136.                                          'nntp_force_fork',
  137.                                          'nntp-force-fork.thtml',
  138.                                          $self );
  139.  
  140.     $self->register_configuration_item_( 'configuration',
  141.                                          'nntp_separator',
  142.                                          'nntp-separator.thtml',
  143.                                          $self );
  144.  
  145.     $self->register_configuration_item_( 'security',
  146.                                          'nntp_local',
  147.                                          'nntp-security-local.thtml',
  148.                                          $self );
  149.  
  150.     if ( $self->config_( 'welcome_string' ) =~ /^NNTP POPFile \(v\d+\.\d+\.\d+\) server ready$/ ) { # PROFILE BLOCK START
  151.         $self->config_( 'welcome_string', "NNTP POPFile ($self->{version_}) server ready" );        # PROFILE BLOCK STOP
  152.     }
  153.  
  154.     return $self->SUPER::start();; }
  155.  
  156. # ----------------------------------------------------------------------------
  157. #
  158. # child__
  159. #
  160. # The worker method that is called when we get a good connection from a client
  161. #
  162. # $client   - an open stream to a NNTP client
  163. # $session        - API session key
  164. #
  165. # ----------------------------------------------------------------------------
  166. sub child__
  167. {
  168.     my ( $self, $client, $session ) = @_;
  169.  
  170.     # Number of messages downloaded in this session
  171.  
  172.     my $count = 0;
  173.  
  174.     # The handle to the real news server gets stored here
  175.  
  176.     my $news;
  177.  
  178.     # The state of the connection (username needed, password needed,
  179.     # authenticated/connected)
  180.  
  181.     my $connection_state = 'username needed';
  182.  
  183.     # Tell the client that we are ready for commands and identify our
  184.     # version number
  185.  
  186.     $self->tee_( $client, "201 " . $self->config_( 'welcome_string' ) .
  187.         "$eol" );
  188.  
  189.     # Retrieve commands from the client and process them until the
  190.     # client disconnects or we get a specific QUIT command
  191.  
  192.     while  ( <$client> ) {
  193.         my $command;
  194.  
  195.         $command = $_;
  196.  
  197.         # Clean up the command so that it has a nice clean $eol at the end
  198.  
  199.         $command =~ s/(\015|\012)//g;
  200.  
  201.         $self->log_( 2, "Command: --$command--" );
  202.  
  203.         # The news client wants to stop using the server, so send that
  204.         # message through to the real news server, echo the response
  205.         # back up to the client and exit the while.  We will close the
  206.         # connection immediately
  207.  
  208.         if ( $command =~ /^ *QUIT/i ) {
  209.             if ( $news )  {
  210.                 last if ( $self->echo_response_( $news, $client, $command ) ==
  211.                          2 );
  212.                 close $news;
  213.             } else {
  214.                 $self->tee_( $client, "205 goodbye$eol" );
  215.             }
  216.             last;
  217.         }
  218.  
  219.         if ($connection_state eq 'username needed') {
  220.  
  221.             # NOTE: This syntax is ambiguous if the NNTP username is a
  222.             # short (under 5 digit) string (eg, 32123).  If this is
  223.             # the case, run "perl popfile.pl -nntp_separator /" and
  224.             # change your kludged username appropriately (syntax would
  225.             # then be server[:port][/username])
  226.  
  227.             my $user_command = '^ *AUTHINFO USER ([^:]+)(:([\d]{1,5}))?(\\' .
  228.                 $self->config_( 'separator' ) . '(.+))?';
  229.  
  230.             if ( $command =~ /$user_command/i ) {
  231.                 my $server = $1;
  232.  
  233.                 # hey, the port has to be in range at least
  234.  
  235.                 my $port = $3 if ( defined($3) && ($3 > 0) && ($3 < 65536) );
  236.                 my $username = $5;
  237.  
  238.                 if ( $server ne '' )  {
  239.                     if ( $news = $self->verify_connected_( $news, $client,
  240.                         $server, $port || 119 ) )  {
  241.                         if (defined $username) {
  242.  
  243.                             # Pass through the AUTHINFO command with
  244.                             # the actual user name for this server, if
  245.                             # one is defined, and send the reply
  246.                             # straight to the client
  247.  
  248.                             $self->get_response_( $news, $client,
  249.                                 'AUTHINFO USER ' . $username );
  250.                             $connection_state = "password needed";
  251.                         } else {
  252.  
  253.                             # Signal to the client to send the password
  254.  
  255.                             $self->tee_($client, "381 password$eol");
  256.                             $connection_state = "ignore password";
  257.                         }
  258.                     } else {
  259.                         last;
  260.                     }
  261.                 } else {
  262.                     $self->tee_( $client,
  263.                         "482 Authentication rejected server name not specified in AUTHINFO USER command$eol" );
  264.                     last;
  265.                 }
  266.  
  267.                 $self->flush_extra_( $news, $client, 0 );
  268.                 next;
  269.             } else {
  270.  
  271.                 # Issue a 480 authentication required response
  272.  
  273.                 $self->tee_( $client, "480 Authorization required for this command$eol" );
  274.                 next;
  275.             }
  276.         } elsif ( $connection_state eq "password needed" ) {
  277.             if ($command =~ /^ *AUTHINFO PASS (.*)/i) {
  278.                 my ( $response, $ok ) = $self->get_response_( $news, $client,
  279.                                             $command);
  280.  
  281.                 if ($response =~ /^281 .*/) {
  282.                     $connection_state = "connected";
  283.                 }
  284.                 next;
  285.             } else {
  286.  
  287.                 # Issue a 381 more authentication required response
  288.  
  289.                 $self->tee_( $client, "381 more authentication required for this command$eol" );
  290.                 next;
  291.             }
  292.         } elsif ($connection_state eq "ignore password") {
  293.             if ($command =~ /^ *AUTHINFO PASS (.*)/i) {
  294.                 $self->tee_($client, "281 authentication accepted$eol");
  295.                 $connection_state = "connected";
  296.                 next;
  297.             } else {
  298.  
  299.                 # Issue a 480 authentication required response
  300.  
  301.                 $self->tee_( $client, "381 more authentication required for this command$eol" );
  302.                 next;
  303.             }
  304.         } elsif ( $connection_state eq "connected" ) {
  305.  
  306.             # COMMANDS USED DIRECTLY WITH THE REMOTE NNTP SERVER GO HERE
  307.  
  308.             # The client wants to retrieve an article. We oblige, and
  309.             # insert classification headers.
  310.  
  311.             if ( $command =~ /^ *ARTICLE (.*)/i ) {
  312.                 my ( $response, $ok ) = $self->get_response_( $news, $client,
  313.                                             $command);
  314.                 if ( $response =~ /^220 (.*) (.*)$/i) {
  315.                     $count += 1;
  316.  
  317.                     my ( $class, $history_file ) =
  318.                         $self->{classifier__}->classify_and_modify( $session,
  319.                             $news, $client, 0, '', 0 );
  320.                 }
  321.  
  322.                 next;
  323.             }
  324.  
  325.             # Commands expecting a code + text response
  326.  
  327.             if ( $command =~ 
  328.                 /^ *(LIST|HEAD|BODY|NEWGROUPS|NEWNEWS|LISTGROUP|XGTITLE|XINDEX|XHDR|XOVER|XPAT|XROVER|XTHREAD)/i ) {
  329.                 my ( $response, $ok ) = $self->get_response_( $news,
  330.                                             $client, $command);
  331.  
  332.                 # 2xx (200) series response indicates multi-line text
  333.                 # follows to .crlf
  334.  
  335.                 if ( $response =~ /^2\d\d/ ) {
  336.                     $self->echo_to_dot_( $news, $client, 0 );
  337.                 }
  338.                 next;
  339.             }
  340.  
  341.             # Exceptions to 200 code above
  342.  
  343.             if ( $ command =~ /^ *(HELP)/i ) {
  344.                 my ( $response, $ok ) = $self->get_response_( $news, $client,
  345.                                             $command);
  346.                 if ( $response =~ /^1\d\d/ ) {
  347.                     $self->echo_to_dot_( $news, $client, 0 );
  348.                 }
  349.                 next;
  350.             }
  351.  
  352.             # Commands expecting a single-line response
  353.  
  354.             if ( $command =~ 
  355.                 /^ *(GROUP|STAT|IHAVE|LAST|NEXT|SLAVE|MODE|XPATH)/i ) {
  356.                 $self->get_response_( $news, $client, $command );
  357.                 next;
  358.             }
  359.  
  360.             # Commands followed by multi-line client response
  361.  
  362.             if ( $command =~ /^ *(IHAVE|POST|XRELPIC)/i ) {
  363.                 my ( $response, $ok ) = $self->get_response_( $news, $client,
  364.                                             $command);
  365.  
  366.                 # 3xx (300) series response indicates multi-line text
  367.                 # should be sent, up to .crlf
  368.  
  369.                 if ($response =~ /^3\d\d/ ) {
  370.  
  371.                     # Echo from the client to the server
  372.  
  373.                     $self->echo_to_dot_( $client, $news, 0 );
  374.  
  375.                     # Echo to dot doesn't provoke a server response
  376.                     # somehow, we add another CRLF
  377.  
  378.                     $self->get_response_( $news, $client, "$eol" );
  379.                 }
  380.                 next;
  381.             }
  382.         }
  383.  
  384.         # Commands we expect no response to, such as the null command
  385.  
  386.         if ( $ command =~ /^ *$/ ) {
  387.             if ( $news && $news->connected ) {
  388.                 $self->get_response_( $news, $client, $command, 1 );
  389.                 next;
  390.             }
  391.         }
  392.  
  393.         # Don't know what this is so let's just pass it through and
  394.         # hope for the best
  395.  
  396.         if ( $news && $news->connected)  {
  397.             $self->echo_response_($news, $client, $command );
  398.             next;
  399.         } else {
  400.             $self->tee_(  $client, "500 unknown command or bad syntax$eol" );
  401.             last;
  402.         }
  403.     }
  404.  
  405.     if ( defined( $news ) ) {
  406.         $self->done_slurp_( $news );
  407.         close $news;
  408.     }
  409.     close $client;
  410.     $self->mq_post_( 'CMPLT', $$ );
  411.     $self->log_( 0, "NNTP proxy done" );
  412. }
  413.  
  414. # ----------------------------------------------------------------------------
  415. #
  416. # configure_item
  417. #
  418. #    $name            Name of this item
  419. #    $templ           The loaded template that was passed as a parameter
  420. #                     when registering
  421. #    $language        Current language
  422. #
  423. # ----------------------------------------------------------------------------
  424.  
  425. sub configure_item
  426. {
  427.     my ( $self, $name, $templ, $language ) = @_;
  428.  
  429.     if ( $name eq 'nntp_port' ) {
  430.         $templ->param( 'nntp_port' => $self->config_( 'port' ) );
  431.     }
  432.  
  433.     # Separator Character widget
  434.     if ( $name eq 'nntp_separator' ) {
  435.         $templ->param( 'nntp_separator' => $self->config_( 'separator' ) );
  436.     }
  437.  
  438.     if ( $name eq 'nntp_local' ) {
  439.         $templ->param( 'nntp_if_local' => $self->config_( 'local' ) );
  440.      }
  441.  
  442.     if ( $name eq 'nntp_force_fork' ) {
  443.         $templ->param( 'nntp_force_fork_on' => $self->config_( 'force_fork' ) );
  444.     }
  445.  
  446.     #$self->SUPER::configure_item( $name, $language, $session_key );
  447. }
  448.  
  449. # ----------------------------------------------------------------------------
  450. #
  451. # validate_item
  452. #
  453. #    $name            The name of the item being configured, was passed in by the call
  454. #                     to register_configuration_item
  455. #    $templ           The loaded template
  456. #    $language        The language currently in use
  457. #    $form            Hash containing all form items
  458. #
  459. # ----------------------------------------------------------------------------
  460.  
  461. sub validate_item
  462. {
  463.     my ( $self, $name, $templ, $language, $form ) = @_;
  464.  
  465.     if ( $name eq 'nntp_port' ) {
  466.         if ( defined $$form{nntp_port} ) {
  467.             if ( ( $$form{nntp_port} >= 1 ) && ( $$form{nntp_port} < 65536 ) ) {
  468.                 $self->config_( 'port', $$form{nntp_port} );
  469.                 $templ->param( 'nntp_port_feedback' => sprintf $$language{Configuration_NNTPUpdate}, $self->config_( 'port' ) );
  470.              } 
  471.              else {
  472.                  $templ->param( 'nntp_port_feedback' => "<div class=\"error01\">$$language{Configuration_Error3}</div>" );
  473.              }
  474.         }
  475.     }
  476.  
  477.     if ( $name eq 'nntp_separator' ) {
  478.         if ( defined $$form{nntp_separator} ) {
  479.             if ( length($$form{nntp_separator}) == 1 ) {
  480.                 $self->config_( 'separator', $$form{nntp_separator} );
  481.                 $templ->param( 'nntp_separator_feedback' => sprintf $$language{Configuration_NNTPSepUpdate}, $self->config_( 'separator' ) );
  482.             } 
  483.             else {
  484.                 $templ->param( 'nntp_separator_feedback' => "<div class=\"error01\">\n$$language{Configuration_Error1}</div>\n" );
  485.             }
  486.         }
  487.     }
  488.  
  489.     if ( $name eq 'nntp_local' ) {
  490.         if ( defined $$form{nntp_local} ) {
  491.             $self->config_( 'local', $$form{nntp_local} );
  492.         }
  493.     }
  494.  
  495.  
  496.     if ( $name eq 'nntp_force_fork' ) {
  497.         if ( defined $$form{nntp_force_fork} ) {
  498.             $self->config_( 'force_fork', $$form{nntp_force_fork} );
  499.         }
  500.     }
  501.  
  502.     # $self->SUPER::validate_item( $name, $language, $form );
  503. }
  504.  
  505. 1;
  506.